package com.project.common.filter;

import com.project.common.orderby.OrderPattern;
import com.project.common.transfer.AbstractComparableNumber;
import com.project.common.transfer.ComparableString;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

@SuppressWarnings("unchecked")
public abstract class AbstractFilter<K extends Serializable, F extends AbstractFilter<K, F>>
        implements Filter<K, F> {
    private boolean distinct;
    private List<Criteria> criteria;
    private Criteria current;
    private Criteria previous;

    private List<OrderPattern> orderPatternList;

    List<String> groupByList;

    protected AbstractFilter() {
        this(false);
    }

    protected AbstractFilter(boolean distinct) {
        this.distinct = distinct;
        this.criteria = new ArrayList<>();
        this.current = new Criteria(Logic.NONE);
        this.criteria.add(this.current);
        this.orderPatternList = new LinkedList<>();
        this.groupByList = new ArrayList<>();
    }

    @Override
    public F and(Criterion criterion) {
        this.current.addCriterion(criterion);
        return (F) this;
    }

    @Override
    public F or(Criterion criterion) {
        this.current.addCriterion(Logic.OR, criterion);
        return (F) this;
    }

    @Override
    public F andNew() {
        this.previous = this.current;
        this.current = new Criteria(Logic.AND);
        this.criteria.add(this.current);
        return (F) this;
    }

    @Override
    public F orNew() {
        this.previous = this.current;
        this.current = new Criteria(Logic.OR);
        this.criteria.add(this.current);
        return (F) this;
    }

    @Override
    public F endNew() {
        this.current = this.previous;
        this.previous = null;
        return (F) this;
    }

    @Override
    public F withKey(K key) {
        return and(Criterion.equal("`t`.`id`", key));
    }

    public List<Criteria> getCriteria() {
        return criteria;
    }

    @Override
    public F distinct() {
        return distinct(true);
    }

    public F distinct(boolean distinct) {
        this.distinct = distinct;
        return (F) this;
    }

    public void reset() {
        this.criteria.clear();
        this.distinct = false;
        this.current = new Criteria(Logic.NONE);
        this.criteria.add(this.current);
        this.orderPatternList.clear();
        this.groupByList.clear();
    }

    public F andStringListFilter(List<ComparableString> stringList) {
        //等于、不等于、开始等于、结束等于、包含、不包含
        for (ComparableString comparableString : stringList) {
            String columnName = comparableString.getName();
            if (!StringUtils.isEmpty(comparableString.getCompareDirectionStart())
                    && !StringUtils.isEmpty(comparableString.getCompareDirectionEnd())) {
                this.andNew();
            }
            if (!StringUtils.isEmpty(comparableString.getCompareDirectionStart())) {
                switch (comparableString.getCompareDirectionStart()) {
                    case "1":
                        and(Criterion.equal(columnName, comparableString.getStartValue()));
                        break;
                    case "2":
                        and(Criterion.notEqual(columnName, comparableString.getStartValue()));
                        break;
                    case "3":
                        and(Criterion.startWith(columnName, comparableString.getStartValue()));
                        break;
                    case "4":
                        and(Criterion.endWith(columnName, comparableString.getStartValue()));
                        break;
                    case "5":
                        and(Criterion.contains(columnName, comparableString.getStartValue()));
                        break;
                    case "6":
                        and(Criterion.notContains(columnName, comparableString.getStartValue()));
                        break;
                    default:
                        return (F) this;
                }
            }

            if (!StringUtils.isEmpty(comparableString.getCompareDirectionEnd())) {
                switch (comparableString.getCompareDirectionEnd()) {
                    case "1":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.equal(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.equal(columnName, comparableString.getEndValue()));
                        }
                        break;
                    case "2":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.notEqual(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.notEqual(columnName, comparableString.getEndValue()));
                        }
                        break;
                    case "3":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.startWith(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.startWith(columnName, comparableString.getEndValue()));
                        }
                        break;
                    case "4":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.endWith(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.endWith(columnName, comparableString.getEndValue()));
                        }
                        break;
                    case "5":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.contains(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.contains(columnName, comparableString.getEndValue()));
                        }
                        break;
                    case "6":
                        if (comparableString.andOr.equals("1")) {
                            and(Criterion.notContains(columnName, comparableString.getEndValue()));
                        } else {
                            or(Criterion.notContains(columnName, comparableString.getEndValue()));
                        }
                        break;
                    default:
                        return (F) this;
                }
            }
            if (!StringUtils.isEmpty(comparableString.getCompareDirectionStart())
                    && !StringUtils.isEmpty(comparableString.getCompareDirectionEnd())) {
                this.andNew();
            }
        }
        return (F) this;
    }

    public F andNumberListFilter(List<AbstractComparableNumber> numberList) {
        //大于、大于或等于、小于、小于或等于、等于、不等于
        for (AbstractComparableNumber comparableNumber : numberList) {
            String columnName = comparableNumber.getName();
            if (!StringUtils.isEmpty(comparableNumber.getCompareDirectionStart())
                    && !StringUtils.isEmpty(comparableNumber.getCompareDirectionEnd())) {
                this.andNew();
            }
            if (!StringUtils.isEmpty(comparableNumber.getCompareDirectionStart())) {
                switch (comparableNumber.getCompareDirectionStart()) {
                    case "1":
                        and(Criterion.greatThan(columnName, comparableNumber.getStartValue()));
                        break;
                    case "4":
                        and(Criterion.greatThanOrEqual(columnName, comparableNumber.getStartValue()));
                        break;
                    case "3":
                        and(Criterion.lessThan(columnName, comparableNumber.getStartValue()));
                        break;
                    case "5":
                        and(Criterion.lessThanOrEqual(columnName, comparableNumber.getStartValue()));
                        break;
                    case "2":
                        and(Criterion.equal(columnName, comparableNumber.getStartValue()));
                        break;
                    case "6":
                        and(Criterion.notEqual(columnName, comparableNumber.getStartValue()));
                        break;
                    default:
                        return (F) this;
                }
            }

            if (!StringUtils.isEmpty(comparableNumber.getNameAddition())) {
                columnName = comparableNumber.getNameAddition();
            }

            if (!StringUtils.isEmpty(comparableNumber.getCompareDirectionEnd())) {
                switch (comparableNumber.getCompareDirectionEnd()) {
                    case "1":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.greatThan(columnName, comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.greatThan(columnName, comparableNumber.getEndValue()));
                        }
                        break;
                    case "4":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.greatThanOrEqual(columnName, comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.greatThanOrEqual(columnName, comparableNumber.getEndValue()));
                        }
                        break;
                    case "3":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.lessThan(columnName, comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.lessThan(columnName, comparableNumber.getEndValue()));
                        }
                        break;
                    case "5":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.lessThanOrEqual(columnName, comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.lessThanOrEqual(columnName, comparableNumber.getEndValue()));
                        }
                        break;
                    case "2":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.equal(columnName, comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.equal(columnName, comparableNumber.getEndValue()));
                        }
                        break;
                    case "6":
                        if (comparableNumber.andOr.equals("1")) {
                            and(Criterion.notEqual(comparableNumber.getName(), comparableNumber.getEndValue()));
                        } else {
                            or(Criterion.notEqual(comparableNumber.getName(), comparableNumber.getEndValue()));
                        }
                        break;
                    default:
                        return (F) this;
                }
            }
            if (!StringUtils.isEmpty(comparableNumber.getCompareDirectionStart())
                    && !StringUtils.isEmpty(comparableNumber.getCompareDirectionEnd())) {
                this.andNew();
            }
        }
        return (F) this;
    }

    public F orderBy(OrderPattern orderPattern) {
        this.orderPatternList.add(orderPattern);
        return (F) this;
    }

    public F groupBy(String columnName) {
        this.groupByList.add(columnName);
        return (F) this;
    }
}
